package us.versus.them.weeds;

import flash.display.Graphics;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.Lib;

class App {
	var weeds:Array< Weed >;
	var drops:Array< Vertlet >;

	var isRaining : Bool;
	var gravity : Float;
	var damping : Float;
	var timeStep : Float;

	public function new() {
		init();
		Lib.current.addEventListener( Event.ENTER_FRAME, loop );
		Lib.current.stage.addEventListener( MouseEvent.MOUSE_DOWN, mouseDown );
	}

	public function init() {
		gravity = 0.9;
		damping = 1;
		timeStep = 1;

		isRaining = true;
		drops = new Array< Vertlet >();
		for ( i in 0 ... 100 ) {
			drops.push( new Vertlet( 640 * Math.random(), - 480 * Math.random() * 3 ) );
		}

		weeds = new Array< Weed >();

		if ( true ) {
			var n = 20;
			var i = n;
			while ( i < 640 ) {
				var x = i + 30 * ( 0.5 - Math.random() );
				var y = 420 + 30 * ( 0.5 - Math.random() );
				var q = 2 + Math.floor( 3 * Math.random() );
				for ( j in 0 ... q ) {
					weeds.push( new Weed( x, y, 24, 6 ) );
				}
				i+= n;
			}
		} else {
			weeds.push( new Weed( 320, 400, 24, 5 ) );
		}

		weeds.sort( function( a:Weed, b:Weed ) { return Math.floor( a.y - b.y ); } );
	}

	public function loop( event : Event ) {
		drawLoop( flash.Lib.current.graphics );
	}

	public function drawLoop( graphics:Graphics ) {
		graphics.clear();

		drawSky( graphics );
		drawGround( graphics );

		for ( weed in weeds ) {
			weed.move( gravity, damping, timeStep );
			weed.draw( graphics );
		}

		if ( isRaining ) {
			drawRain( graphics );
			rain();
		}
	}

	public function drawSky( graphics:Graphics ) {
		graphics.lineStyle( 0, 0xAAAAFF );
		graphics.beginFill( 0xAAAAFF );
		graphics.drawRect( 0, 0, 640, 400 );
		graphics.endFill();
	}

	public function drawGround( graphics:Graphics ) {
		// karina wants some ground for our flowers...
		graphics.lineStyle( 0, 0xAA8844 );
		graphics.beginFill( 0xAA8844 );
		graphics.drawRect( 0, 390, 640, 481 );
		graphics.endFill();
	}

	public function drawRain( graphics:Graphics ) { 
		for ( drop in drops ) {
			drop.move( damping, timeStep );
			drop.force.y = gravity;
			graphics.lineStyle( 1, 0x0000FF );
			graphics.beginFill( 0x0000FF );

			graphics.moveTo( drop.current.x, drop.current.y );
			graphics.lineTo( drop.current.x, drop.current.y - 2.5 );

			graphics.drawCircle( drop.current.x, drop.current.y, 1 );

			if ( drop.current.y > 400 ) {
				drop.current.y = drop.previous.y = - 20;
			}
		}
	}

	public function rain() {
		var min = 1234.5678;
		for ( weed in weeds ) {
			for ( vertlet in weed.vertlets ) {
				if ( vertlet.current.y < min ) {
					min = vertlet.current.y;
				}
			}
		}
		var skip_count = 0;

		// this is a bit expensive
		for ( drop in drops ) {
			if ( drop.current.y < min ) {
				// drop is too high to hit anything
				skip_count++;
				continue; 
			}
			for ( weed in weeds ) {
				for ( segment in weed.segments ) {
					var t = intersectionTime( 
						  drop.previous.x, drop.previous.y
						, drop.current.x, drop.current.y
						, segment.start.current.x, segment.start.current.y
						, segment.stop.current.x, segment.stop.current.y
					);
					if ( t > 0 && t < 1 ) {
						var s = intersectionTime(
							  segment.start.current.x, segment.start.current.y
							, segment.stop.current.x, segment.stop.current.y
							, drop.previous.x, drop.previous.y
							, drop.current.x, drop.current.y
						);
						if ( s > 0 && s< 1 ) {
							segment.start.force.y += ( s ) * 4;
							segment.stop.force.y += ( 1 - s ) * 4;
						}
					}
				}
			}
		}
		//trace( 'min = ' + min + ' so ' + skip_count);
	}

	public static function intersectionTime( 
		  line_0_x0:Float, line_0_y0:Float, line_0_x1:Float, line_0_y1:Float
		, line_1_x0:Float, line_1_y0:Float, line_1_x1:Float, line_1_y1:Float
	) {
		var line_0_xdiff = line_0_x1 - line_0_x0;
		var line_0_ydiff = line_0_y1 - line_0_y0;

		var line_1_xdiff = line_1_x1 - line_1_x0;
		var line_1_ydiff = line_1_y1 - line_1_y0;

		var line_1_normal_x = line_1_ydiff;
		var line_1_normal_y = -line_1_xdiff;
		var l2 = line_1_normal_x * line_1_normal_x + line_1_normal_y * line_1_normal_y;
		if ( 0 != l2 ) {
			l2 = Math.sqrt( l2 );
			line_1_normal_x /= l2;
			line_1_normal_y /= l2;
		}

		var denominator = -line_1_normal_x * line_0_xdiff -line_1_normal_y * line_0_ydiff;

		var t = -1.0;
		if ( 0 != denominator ) {
			var numerator = ( 0
				+ line_1_normal_x * ( line_0_x0 - line_1_x0 )
				+ line_1_normal_y * ( line_0_y0 - line_1_y0 )
			);
			t = numerator / denominator;
		}
		return t;
	}

	public function mouseDown( _ ) {
		//this.init();
		isRaining = !isRaining;
	}

}
